package cn.tpshion.devops.handle;

import cn.tpshion.devops.common.enums.MsgType;
import cn.tpshion.devops.domain.entity.MsgData;
import cn.tpshion.devops.node.data.SSHData;
import cn.tpshion.devops.node.support.NodeContext;
import cn.tpshion.devops.util.AssertUtil;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;

@Slf4j
public class SSHHandle implements AutoCloseable {

    private Session session;

    private SSEHandle sseHandle;

    private NodeContext nodeContext;

    public SSHHandle(SSHData sshData, SSEHandle sseHandle, NodeContext nodeContext) throws Exception{
        this.sseHandle = sseHandle;
        this.nodeContext = nodeContext;
        conn(sshData);
    }

    private void conn(SSHData sshData) throws Exception{
        JSch jSch = new JSch();
        Session session = jSch.getSession(sshData.getUsername(), sshData.getHost(), sshData.getPort());
        session.setPassword(sshData.getPassword());
        Properties sshConfig = new Properties();
        sshConfig.put("StrictHostKeyChecking", "no");
        session.setConfig(sshConfig);
        session.connect(3000);
        this.session = session;
    }

    public void exec(String command) throws Exception{
        log.info("SSHHandle exec command:{}", command);
        sseHandle.sendMessage(nodeContext, new MsgData(MsgType.INFO, "SSHHandle exec command: " + command));
        Channel channel = session.openChannel("exec");
        ChannelExec channelExec = (ChannelExec) channel;
        channelExec.setInputStream(null);
        channelExec.setPty(true);
        channelExec.setCommand(command);
        channel.connect(3000);
        handleLog(channelExec);
        int exitStatus = channelExec.getExitStatus();
        log.info("SSHHandle exec exitStatus:{}", exitStatus);
        channelExec.disconnect();
        AssertUtil.isTrue(exitStatus != 0 && exitStatus != -1, "命令执行失败");
    }

    private void handleLog(ChannelExec channel){
        if(Objects.isNull(channel)){
            return;
        }
        try (InputStream inputStream = channel.getInputStream();
             InputStreamReader isr = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
             BufferedReader reader = new BufferedReader(isr)){

            String buffer = null;

            while ((buffer = reader.readLine()) != null) {
                log.info(buffer);
                sseHandle.sendMessage(nodeContext, new MsgData<>(MsgType.INFO, buffer));
            }
        }catch (Exception e){
            log.error("SSHHandle handleLog error:{}", e.getMessage(), e);
        }
    }

    @Override
    public void close() throws Exception {
        if(Objects.nonNull(session)){
            session.disconnect();
        }
    }

    public List<String> handleCommandStr(String command){
        return Arrays.stream(StringUtils.delimitedListToStringArray(command, "\n"))
                .filter(i -> StringUtils.hasText(i))
                .collect(Collectors.toList());
    }
}
